package com.intermancer.predictor.feeder;

import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Date;

import org.exolab.castor.xml.Marshaller;

public class TimeboxedFeederWrapper extends FeederWrapper implements Runnable {

	public static final long DEFAULT_TIMEBOX = 250;
	private long timebox = 0;
	
	private boolean feedingFinished;
	private Thread wrapperThread;
	
	private OutputStream outputStream;

	public TimeboxedFeederWrapper() {
		super();
	}
	
	public TimeboxedFeederWrapper(Feeder feeder) {
		super(feeder);
	}

	@Override
	public void feedOrganism() {
		setWrapperThread(Thread.currentThread());
		Thread thread = new Thread(this);
		long beginTimestamp = new Date().getTime();
		long elapsedTime = 0l;
		thread.start();
		try {
			boolean keepGoing = true;
			while (keepGoing) {
				Thread.sleep(getTimebox());
				elapsedTime = new Date().getTime() - beginTimestamp;
				if (elapsedTime >= getTimebox()) {
					keepGoing = false;
				}
			}
		} catch (InterruptedException ex) {
			// Nothing.  Just means the other thread finished.
		}
		try {
			if (!feedingFinished) {
				thread.stop();
				if (elapsedTime >= getTimebox()){
					Marshaller.marshal(getOrganism(), new OutputStreamWriter(getOutputStream()));
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace(System.err);
		}
	}

	@Override
	public void run() {
		Feeder wrappedFeeder = getFeeder();
		wrappedFeeder.feedOrganism();
		feedingFinished = true;
		getWrapperThread().interrupt();
	}

	@Override
	public void init() {
		super.init();
		feedingFinished = false;
	}

	public long getTimebox() {
		if (timebox == 0) {
			timebox = DEFAULT_TIMEBOX;
		}
		return timebox;
	}

	public void setTimebox(long timebox) {
		this.timebox = timebox;
	}

	public OutputStream getOutputStream() {
		if (outputStream == null) {
			return System.err;
		}
		return outputStream;
	}

	public void setOutputStream(OutputStream outputStream) {
		this.outputStream = outputStream;
	}

	public Thread getWrapperThread() {
		return wrapperThread;
	}

	public void setWrapperThread(Thread wrapperThread) {
		this.wrapperThread = wrapperThread;
	}

}